#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <iostream>

using namespace std;  
using namespace cv;  


void sharpen(Mat& input , Mat& output,double multi = 4, double neighborImportance = 1 );
Mat makeBlue(Mat input , double precision = 0.1 , uchar colorGain = 1);

const uchar BLUE_R = 9;
const uchar BLUE_G = 68;
const uchar BLUE_B = 206;

int main (){

	Mat image, sharpenImg, blue;


	image = imread("C:\\openCVImgs\\motor.jpg");
	if(!image.data){
		cout << "Cannot load image\n";
		getchar();
		return -1;
	}
	
	namedWindow("image");
	imshow("image", image);

	sharpen(image, sharpenImg);
	
	namedWindow("sharpen");
	imshow("sharpen", sharpenImg);

	/*namedWindow("blue");

	double prec = 1;

	while((prec = prec - 0.01) >0){
		blue = makeBlue(sharpenImg,prec,2);
		imshow("blue", blue);
		waitKey(200);
	}*/

	

	//imwrite("C:\\openCVImgs\\blue2.jpg",blue);



	
	double multi = 4;
	double neighborImportance = 0;

	namedWindow("image");
	imshow("image", image);
	namedWindow("sharpen");
	
	while((multi= multi + 0.01)<5){
		sharpen(image, sharpenImg,multi);
		imshow("sharpen", sharpenImg);
		waitKey(200);
	}
	
	
	while((neighborImportance = neighborImportance + 0.01)<3){
		sharpen(image, sharpenImg,multi, neighborImportance);
		imshow("sharpen", sharpenImg);
		waitKey(200);
	}
	
	

	waitKey(0);

	return 1;
}

void sharpen(Mat& input , Mat& output, double multi, double neighborImportance){
	const int channels = input.channels();

	output.create(input.size(), input.type());

	for(int r=1;r<input.rows-1;r++){
		const uchar* prev = input.ptr<uchar>(r-1);
		const uchar* curr = input.ptr<uchar>(r);
		const uchar* next = input.ptr<uchar>(r+1);

		uchar* outRowPtr = output.ptr<uchar>(r);

		for(int c= channels;c < channels * (input.cols-1);c++){
			*outRowPtr++ = saturate_cast<uchar>(multi*curr[c] - (neighborImportance * curr[c-channels]) - (neighborImportance * curr[c+channels]) - (neighborImportance * prev[c]) - (neighborImportance * next[c]));	
		}

	}

	output.row(0).setTo(Scalar(0));
	output.row(output.rows-1).setTo(Scalar(0));
	output.col(0).setTo(Scalar(0));
	output.col(output.cols-1).setTo(Scalar(0));
}

Mat makeBlue(Mat input , double precision , uchar colorGain ){

	CV_Assert(input.channels() == 3);

	Mat output;
	output.create(input.size(), input.type());

	
	uchar* row;
	uchar* outRow;

	uchar blue,red,green;

	int maxColor = 0;
	int tmpColor =0;
	int tmpMultCol =0;
	
	double colorPower =0;
	double colorMult = 0;
	double newColorMult = 0;

	for(int r=0;r<input.rows;r++){
		row = input.ptr<uchar>(r);
		for(int c=0;c<input.cols;c++){
			tmpMultCol = c*input.channels();
			tmpColor = row[tmpMultCol] + row[tmpMultCol+1] + row[tmpMultCol+2];
			if(maxColor < tmpColor){maxColor = tmpColor;}
		}
	}




	for(int r=0;r<input.rows;r++){
		output.row(r).setTo(255);
		row = input.ptr<uchar>(r);
		outRow = output.ptr<uchar>(r);
		for(int c=0;c<input.cols;c++){
			colorPower = 0;
			for(int ch=0;ch<input.channels();ch++){
				colorPower+= row[c*input.channels()+ch];
			}

			colorMult = colorPower / maxColor;
			//double newColorPower = (colorPower / (colorScale*maxColor)) + 1/colorScale;
			newColorMult = 1 - (colorPower / (colorGain*maxColor)) ;
			if(colorMult > precision){

				blue = newColorMult * BLUE_B ;
				red = newColorMult * BLUE_G ;
				green = newColorMult * BLUE_R;

				outRow[c*input.channels()] = blue;
				outRow[c*input.channels()+1] = green;
				outRow[c*input.channels()+2] = red;
			}

		}
	}


	return output;
}
